/*
 * Copyright (c) 2007 T.Cogghe, D.Leroux, S.Lévignac, S.Rives
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __ASTRE_H__
#define __ASTRE_H__

#include "Id.hh"
#include "Image.hh"
#include <cstring>
#include <cmath>
#include <map>

#define TAILLE_CARRE_ASTRE (sizeof(int)*8)

#define MOITIE_CARRE_ASTRE (sizeof(int)*4)

/*! Représente un astre trouvé dans une image.
 * La class Astre recherche, délimite, et identifie des Astres dans les images.
 */
class Astre {
public:
	/*! Identifie un astre.
	 * Permet de déterminer dans quelle mesure deux astres venant de deux images différentes se ressemblent
	 */
	class Identite : public Id {
	private:
		/*! tolérance sur l'écart pour la comparaison */
		static double _tol;
		/*! teinte globale de l'astre */
		double r,g,b;
		/*! somme des luminosités */
		double luminositeTotale;
		/*! sigma sur pixels masqués de (luminosité*distance au centre)/luminosité totale*nb_pixels */
		double repartition;
	public:
		/*! crée l'identité d'un astre */
		Identite(Astre&a);
		~Identite() {}

		
		/*! compare deux identités (prédicat "ressemble à") */
		bool operator == (const Id&ib) const {
			const Identite&i=*dynamic_cast<const Identite*>(&ib);
			double delta=(*this)%i;
//			cout<<"id's == ? : "<<delta<<endl;
			return delta<=_tol;
		}

		/*! estime l'éloignement entre deux identités */
		double operator % (const Id&ib) const {
			const Identite*i=dynamic_cast<const Identite*>(&ib);

			double drep=Id::deltaPourcents(repartition,i->repartition);
			double dr=Id::deltaPourcents(r,i->r);
			double dg=Id::deltaPourcents(g,i->g);
			double db=Id::deltaPourcents(b,i->b);

//			cout<<"comp id's : drep="<<drep
//				<<" dr="<<dr<<" dg="<<dg<<" db="<<db<<endl;

			return ((1+drep)*(1+dr)*(1+dg)*(1+db))-1;	/* les erreurs s'amplifient entre elles */
		}
	};

private:
	/*! le type du masque de l'astre.
	 * un carré de booléens marquant si le pixel correspondant dans l'image appartient à l'astre ou non.
	 * il est important que le masque soit au moins deux fois plus grand que les objets que l'on sélectionnera
	 */
	typedef unsigned int TMasque[TAILLE_CARRE_ASTRE];	/* un booléen par case du carré, condensé dans un bit */

	/*! l'identité de l'astre */
	Identite* _id;

	/*! l'image d'où vient l'astre */
	const Image&pic;
	/*! coordonnées du centre */
	double org_x;
	/*! coordonnées du centre */
	double org_y;
	/*! position du centre du masque dans l'image */
	Position org_masque;
	/*! le masque de l'astre */
	TMasque msk;
	//TMasque ctr;
	/*! bornes du contour de l'astre */
	int ctr_xmin,ctr_xmax,ctr_ymin,ctr_ymax;

	/*! accès direct en lecture à un bit d'un masque */
	inline int _rawget(const TMasque m,int x,int y) const {
		y+=MOITIE_CARRE_ASTRE;
		x+=MOITIE_CARRE_ASTRE;
		return (m[y]>>x)&1;
	}

	/*! accès direct en écriture à un bit d'un masque */
	inline void _rawset(TMasque m,int x,int y,const unsigned int b) {
		y+=MOITIE_CARRE_ASTRE;
		x+=MOITIE_CARRE_ASTRE;
		m[y] &= ~(1<<x);
		m[y] |=  (b<<x);
	}

	/*! lecture d'un bit du masque */
	inline int masque(const int x,const int y) const { return _rawget(msk,x,y); }	/* simplement une fermeture */
	/*! écriture d'un bit du masque */
	inline void masque(const int x,const int y,const unsigned char b) { _rawset(msk,x,y,b?1:0); }

	//inline int contour(int x,int y) { return _rawget(ctr,x,y); }
	//inline void contour(int x,int y,unsigned char b) { _rawset(ctr,x,y,b); }

	/*! détermine tous les pixels qui appartiennent à l'astre, en fonction du \par seuil */
	void delimiterObjet(const int seuil);
	/*! appxoximationne (sic) le centre de l'astre à partir du tas de pixels délimité */
	void determinerCentre();
	
	/*! d'oh ! un constructeur privé ! on crée des astres via trouverAutourDe() et trouverDansRectangle(). */
	Astre(const Image&img,const Position pos,const int seuil) : pic(img) {
		org_masque.x=pos.x;
		org_masque.y=pos.y;
		//for(int i=0;i<TAILLE_CARRE_ASTRE;i++) msk[i]=ctr[i]=0;
		memset(msk,0,sizeof(msk));
		//memset(ctr,0,sizeof(ctr));
		delimiterObjet(seuil);
		determinerCentre();
		_id=new Identite(*this);
	}

public:
	~Astre() { delete _id; }

	/*! trace un carré (pointillé si \par incr>1) autour de l'astre dans l'image \par copie. */
	void tracerContour(Image&copie,const int incr=1) const;
	/*! noircit tous les pixels masqués par l'astre dans l'image copie */
	void effacer(Image&copie) const;

	/*! trouve un astre.
	 * lance une tête chercheuse autour de \par x,\par y jusqu'à trouver un astre ou s'essoufler. */
	static Astre*trouverAutourDe(Image& pic,int x,int y,int seuil);

	/*! trouve plein d'astres.
	 * découvre tous les astres découvrables dans un rectangle de
	 * l'image \par source, et les range dans le \par sac. Le sac
	 * est trié par ressemblance décroissante à \par aiguille.
	 */
	static int trouverDansRectangle(const Image& source,const Astre*aiguille,int x,int y,int l,int h,const int seuil,multimap<double,Astre*>& sac);

	/*! coordonnées du centre de l'astre */
	double x() const { return org_x; }
	/*! coordonnées du centre de l'astre */
	double y() const { return org_y; }

	int operator ==(Astre&);

	/*! accès à l'identité de l'astre */
	Identite&id() const { return *_id; }
};



#endif
